﻿using System;
using vm.Aspects.Visitor;

namespace VisitorExample
{
    abstract class BaseVisited : IVisited<BaseVisited>
    {
        public BaseVisited Accept(IVisitor<BaseVisited> visitor)
        {
            if (visitor == null)
                throw new ArgumentNullException("visitor");

            Console.WriteLine("{0} accepts {1} visitor.", GetType().Name, visitor.GetType().Name);
            visitor.Visit(this);
            return this;
        }
    }

    class Derived1 : BaseVisited, IVisited<Derived1>
    {
        public Derived1 Accept(IVisitor<Derived1> visitor)
        {
            if (visitor == null)
                throw new ArgumentNullException("visitor");

            Console.WriteLine("{0} accepts {1} visitor.", GetType().Name, visitor.GetType().Name);
            visitor.Visit(this);
            return this;
        }
    }

    class Derived2 : BaseVisited
    {
    }

    class Other : IVisited<Other>
    {
        public Other Accept(IVisitor<Other> visitor)
        {
            if (visitor == null)
                throw new ArgumentNullException("visitor");

            Console.WriteLine("{0} accepts {1} visitor.", GetType().Name, visitor.GetType().Name);
            visitor.Visit(this);
            return this;
        }
    }

    class HasNoVisitor : IVisited<HasNoVisitor>
    {
        public HasNoVisitor Accept(IVisitor<HasNoVisitor> visitor)
        {
            if (visitor == null)
                throw new ArgumentNullException("visitor");

            Console.WriteLine("{0} accepts {1} visitor.", GetType().Name, visitor.GetType().Name);
            visitor.Visit(this);
            return this;
        }
    }

    class ExampleVisitor : CatchallVisitor,
                           IVisitor<BaseVisited>,
                           IVisitor<Derived1>,
                           IVisitor<Other>
    {
        void IVisitor<BaseVisited>.Visit(BaseVisited visited)
        {
            if (visited == null)
                throw new ArgumentNullException("visited");

            Console.WriteLine("Visitor {0} visits {1} object.", GetType().Name, visited.GetType().Name);
        }

        void IVisitor<Derived1>.Visit(Derived1 visited)
        {
            if (visited == null)
                throw new ArgumentNullException("visited");

            Console.WriteLine("Visitor {0} visits {1} object.", GetType().Name, visited.GetType().Name);
        }

        void IVisitor<Other>.Visit(Other visited)
        {
            if (visited == null)
                throw new ArgumentNullException("visited");

            Console.WriteLine("Visitor {0} visits {1} object.", GetType().Name, visited.GetType().Name);
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                var derived1 = new Derived1();
                var derived2 = new Derived2();
                var other    = new Other();
                var hasNone  = new HasNoVisitor();
                var visitor  = new ExampleVisitor();

                derived1.Accept(visitor);
                derived2.Accept(visitor);
                other.Accept(visitor);
                //hasNone.Accept(visitor);
            }
            catch (NotImplementedException x)
            {
                Console.WriteLine("EXCEPTION: {0}", x.Message);
            }

            Console.Write("Press any key to exit...");
            Console.ReadKey(true);
        }
    }
}
